﻿#include "directx_test.h"
#include <dx_graphics.h>
#include <format>
#include <iostream>
#include <map>
#include <numeric>
#include <string>
#include <vector>
#include "../window/window_app.h"

using namespace DirectX;
using Microsoft::WRL::ComPtr;

namespace ifire {

constexpr XMVECTORF32 EPSILON = {0.001f, 0.001f, 0.001f, 0.001f};

void DirectXTest::PrintGeneralVectorFunction() {
  XMFLOAT2 f2(1, 2);
  XMFLOAT3 f3(1, 2, 3);
  XMFLOAT4 f4(1, 1, 1, 0);

  XMVECTOR v2 = XMLoadFloat2(&f2);
  XMVECTOR v3 = XMLoadFloat3(&f3);
  XMVECTOR v4 = XMLoadFloat4(&f4);

  XMStoreFloat2(&f2, v2);
  XMStoreFloat3(&f3, v3);
  XMStoreFloat4(&f4, v4);

  PrintVector("Zero", XMVectorZero());
  PrintVector("SplatOne", XMVectorSplatOne());
  PrintVector("Set", XMVectorSet(1.0f, 2.0f, 3.0f, 0.0f));
  PrintVector("Replicate", XMVectorReplicate(-2.0f));
  PrintVector("SplatZ", XMVectorSplatZ(v4));

  PrintVector("Length", XMVector3Length(v2));
  PrintVector("LengthSq", XMVector3LengthSq(v2));
  PrintVector("Dot", XMVector3Dot(v3, v4));
  PrintVector("Cross", XMVector3Cross(v3, v4));
  PrintVector("Normalize", XMVector3Normalize(v3));
  PrintVector("Orthogona", XMVector3Orthogonal(v3));
  PrintVector("Angle", XMVector3AngleBetweenVectors(v3, v4));

  XMVECTOR parallel, perpendicular;
  XMVector3ComponentsFromNormal(&parallel, &perpendicular, v2,
                                XMVector3Normalize(v2));
  PrintVector("Paralle", parallel);
  PrintVector("Perpendicular", perpendicular);

  PrintLine("Is equal: %d", XMVector3Equal(v2, v2));
  PrintLine("Is not equal: %d", XMVector3NotEqual(v2, v2));
}

void DirectXTest::VectorCalculate() {
  PrintLine("----向量计算----");

  XMVECTOR n = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f);
  XMVECTOR u = XMVectorSet(1.0f, 2.0f, 3.0f, 0.0f);
  XMVECTOR v = XMVectorSet(-2.0f, 1.0f, -3.0f, 0.0f);
  XMVECTOR w = XMVectorSet(0.707f, 0.707f, 0.0f, 0.0f);

  PrintVector("n", n);
  PrintVector("u", u);
  PrintVector("v", v);
  PrintVector("w", w);

  // 向量加法
  XMVECTOR a = u + v;
  PrintVector("向量加法(u+v)", a);
  // 向量减法
  XMVECTOR b = u - v;
  PrintVector("向量减法(u-v)", b);
  // 标量乘法
  XMVECTOR c = 10.0f * u;
  PrintVector("标量乘法(10.0f*u)", c);
  // 计算长度||u||
  XMVECTOR l = XMVector3Length(u);
  PrintVector("计算长度(u)", l);
  // 计算长度估算值
  XMVECTOR l_est = XMVector3LengthEst(u);
  PrintVector("计算长度估算值(u)", l_est);
  // 归一化
  XMVECTOR d = XMVector3Normalize(u);
  PrintVector("归一化(u)", d);
  // 归一化的近似值（如果这个不等于1，不会出问题吗？）
  XMVECTOR d_est = XMVector3NormalizeEst(u);
  PrintVector("归一化的近似值(u)", d_est);
  // 点乘
  XMVECTOR dot = XMVector3Dot(u, v);
  PrintVector("点乘(u,v)", dot);
  // 叉乘
  XMVECTOR cross = XMVector3Cross(u, v);
  PrintVector("叉乘(u,v)", cross);

  // 求出proj_n（投影）和perp_n（垂直）
  XMVECTOR projW;
  XMVECTOR perpW;
  XMVector3ComponentsFromNormal(&projW, &perpW, w, n);
  PrintVector("投影(w)", projW);
  PrintVector("垂直(w)", perpW);

  // 垂直与投影相加应该等于原值
  Assert("XMVector3Equal ProjW + PerpW == w?",
         XMVector3Equal(projW + perpW, w));
  Assert("XMVector3NotEqual ProjW + PerpW == w?",
         XMVector3NotEqual(projW + perpW, w));

  // 垂直与投影之间的夹角应该是90度
  XMVECTOR angleVec = XMVector3AngleBetweenVectors(projW, perpW);
  float angleRadians = XMVectorGetX(angleVec);
  float angleDegrees = XMConvertToDegrees(angleRadians);
  PrintFloat("夹角(projW,perpW)弧度", angleRadians);
  PrintFloat("夹角(projW,perpW)角度", angleRadians);

  // 使用近似值比较浮点数
  XMVECTOR n1 = XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f);
  XMVECTOR u1 = XMVectorSet(1.0f, 1.0f, 0.999f, 0.0f);
  XMVECTOR u2 = XMVectorSet(1.0f, 1.0f, 0.99f, 0.0f);
  PrintVector("n1", n1);
  PrintVector("u1", u1);
  PrintVector("u2", u2);
  PrintVector("EPSILON", EPSILON);
  Assert("XMVector3NearEqual(n1,u1)", XMVector3NearEqual(n1, u1, EPSILON));
  Assert("XMVector3NearEqual(n1,u2)", XMVector3NearEqual(n1, u2, EPSILON));
}

void DirectXTest::MatrixCalculate() {
  PrintLine("---- 矩阵计算 ----");

  XMMATRIX A(1.0f, 0.0f, 0.0f, 0.0f,  // 1
             0.0f, 2.0f, 0.0f, 0.0f,  // 2
             0.0f, 0.0f, 4.0f, 0.0f,  // 3
             1.0f, 2.0f, 3.0f, 1.0f); // 4
  PrintMatrix("A", A);

  XMMATRIX B = XMMatrixIdentity();
  PrintMatrix("B:(单位矩阵)", B);

  XMMATRIX C = A * B;
  PrintMatrix("C:(A * B)", C);

  XMMATRIX D = XMMatrixTranspose(A);
  PrintMatrix("D:(A的转置矩阵)", D);

  // 求逆矩阵，它居然不能直接求，必须先计算出行列式
  XMVECTOR det = XMMatrixDeterminant(A);
  PrintVector4("detA:(A行列式)", det);
  XMMATRIX E = XMMatrixInverse(&det, A);
  PrintMatrix("E:(A的逆矩阵)", E);

  // 构建一个缩放矩阵
  XMMATRIX scale_mat = XMMatrixScaling(1.0f, 1.0f, 1.0f);
  PrintMatrix("缩放矩阵", E);
  // 使用3D向量中的分量来构架
  XMVECTOR vec1 = XMVectorSet(1.0f, 2.0f, 3.0f, 4.0f);
  XMMATRIX scale_by_vec = XMMatrixScalingFromVector(vec1);
  PrintVector4("使用3D向量构架(向量1)", vec1);
  PrintMatrix("使用3D向量构架(缩放矩阵)", scale_by_vec);

  // 分别使用X,Y,Z来构架旋转（按轴旋转）
  XMMATRIX x_rotation = XMMatrixRotationX(XMConvertToRadians(90.0f));
  XMMATRIX y_rotation = XMMatrixRotationY(XMConvertToRadians(90.0f));
  XMMATRIX z_rotation = XMMatrixRotationZ(XMConvertToRadians(90.0f));
  PrintMatrix("使用X构架(旋转矩阵)", x_rotation);
  PrintMatrix("使用Y构架(旋转矩阵)", y_rotation);
  PrintMatrix("使用Z构架(旋转矩阵)", z_rotation);
  // 绕任意轴旋转的矩阵
  XMVECTOR axis = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f);
  XMMATRIX axis_rotation =
      XMMatrixRotationAxis(axis, XMConvertToRadians(90.0f));
  PrintVector4("绕任意轴旋转(向量)", axis);
  PrintMatrix("绕任意轴旋转(旋转矩阵)", axis_rotation);
  // 创建平移矩阵
  XMMATRIX translation = XMMatrixTranslation(1.0f, 2.0f, 3.0f);
  XMMATRIX translation_by_vec = XMMatrixTranslationFromVector(vec1);
  PrintMatrix("平移矩阵", translation);
  PrintMatrix("平移矩阵(通过向量创建)", translation_by_vec);

  // 计算向量与矩阵的乘积vM，使Vw = 1，从而使位移有效
  XMVECTOR pos1 = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f);
  PrintVector4("位置(向量)", pos1);
  XMVECTOR coord_transform = XMVector3TransformCoord(pos1, translation_by_vec);
  // 此函数同上，但是通过使Vw = 0，跳过了位移。
  XMVECTOR normal_transform =
      XMVector3TransformNormal(pos1, translation_by_vec);
  PrintVector4("点变换", coord_transform);
  PrintVector4("向量变换", normal_transform);
}

bool DirectXTest::CheckSSE2() {
  if (!XMVerifyCPUSupport()) {
    PrintLine("DirectX math not supported.");
    return false;
  } else {
    PrintLine("DirectX math supported.");
    return true;
  }
}

void DirectXTest::CheckFeatureSupport() {
  PrintLine("---- 检查特性支持 ----");

  D3D_FEATURE_LEVEL featureLevels[4] = {
      D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_12_2, //
      D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0, //
      D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_10_0, //
      D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_9_3   //
  };
  D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevelsInfo;
  featureLevelsInfo.NumFeatureLevels = 4;
  featureLevelsInfo.pFeatureLevelsRequested = featureLevels;
}

void DirectXTest::LogAdapters() {
  PrintLine("---- 遍历显示适配器 ----");

  UINT i = 0;
  ComPtr<IDXGIAdapter> adapter;

  IDXGIFactory* dxgi_factory = nullptr;
  Assert("CreateDXGIFactory",
         CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&dxgi_factory)));
  while (dxgi_factory->EnumAdapters(i, adapter.GetAddressOf()) !=
         DXGI_ERROR_NOT_FOUND) {
    DXGI_ADAPTER_DESC desc;
    adapter->GetDesc(&desc);
    auto name = wchar_to_char(desc.Description);
    PrintLine("Adapter: %s", name.c_str());
    LogAdapterOutputs(adapter.Get());
    i++;
  }
}

void DirectXTest::LogAdapterOutputs(IDXGIAdapter* adapter) {
  UINT i = 0;
  ComPtr<IDXGIOutput> output;
  while (adapter->EnumOutputs(i, output.GetAddressOf()) !=
         DXGI_ERROR_NOT_FOUND) {
    DXGI_OUTPUT_DESC desc;
    output->GetDesc(&desc);
    auto name = wchar_to_char(desc.DeviceName);
    PrintLine("  -Device: %s", name.c_str());

    LogOutputDisplayModes(output.Get(), DXGI_FORMAT_B8G8R8A8_UNORM);

    output.Reset();
    i++;
  }
}

void DirectXTest::LogOutputDisplayModes(IDXGIOutput* output,
                                        DXGI_FORMAT format) {
  UINT count = 0;
  UINT flags = 0;
  output->GetDisplayModeList(format, flags, &count, nullptr);
  std::vector<DXGI_MODE_DESC> modeList(count);
  output->GetDisplayModeList(format, flags, &count, &modeList[0]);

  std::map<std::pair<int, int>, std::vector<std::string>> modes;

  for (auto& x : modeList) {
    auto size = std::make_pair<int, int>(x.Width, x.Height);

    float refresh = static_cast<float>(x.RefreshRate.Numerator) /
                    static_cast<float>(x.RefreshRate.Denominator);
    modes[size].push_back(std::format("{:.1f}", refresh));
  }

  for (const auto& kv : modes) {
    const auto& key = kv.first;
    const auto& value = kv.second;
    std::string result =
        std::accumulate(value.begin(), value.end(), std::string(),
                        [](const std::string& a, const std::string& b) {
                          return a.empty() ? b : a + ", " + b;
                        });

    PrintLine("    - 分辨率: %dx%d 刷新率: %s", key.first, key.second,
              result.c_str());
  }
}

void DirectXTest::DirectXInitialization() {
  PrintLine("---- 初始化DirectX ----");
  DxGraphics dx;
  auto main_win = WindowApp::GetApp();
  auto win = main_win->GetDxWindow();

  PrintLine("初始化已经完成！");
}

void DirectXTest::RunTest() {
   

  // 检查是否支持SSE2指令集
  if (!CheckSSE2()) {
    return;
  }

  // PrintGeneralVectorFunction();
  // VectorCalculate();
  // MatrixCalculate();
  //LogAdapters();
  DirectXInitialization();

  FlushLog();
}

} // namespace ifire